package com.atguigu.userprofile.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.userprofile.bean.TagCondition;
import com.atguigu.userprofile.bean.TagInfo;
import com.atguigu.userprofile.bean.UserGroup;
import com.atguigu.userprofile.constants.ConstCodes;
import com.atguigu.userprofile.mapper.UserGroupMapper;
import com.atguigu.userprofile.service.TagInfoService;
import com.atguigu.userprofile.service.UserGroupService;
import com.atguigu.userprofile.utils.RedisUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;

import org.apache.catalina.User;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author zhangchen
 * @since 2021-05-04
 */
@Service
@Slf4j
@DS("mysql")
public class UserGroupServiceImpl extends ServiceImpl<UserGroupMapper, UserGroup> implements UserGroupService {

    @Autowired
    TagInfoService tagInfoService;


    @Override
    public void saveUserGroupInfo(UserGroup userGroup) {
        String conditionJson = JSON.toJSONString(userGroup.getTagConditions());
        userGroup.setConditionJsonStr(conditionJson);
        userGroup.setConditionComment( userGroup.conditionJsonToComment());
        userGroup.setCreateTime(new Date());
        this.saveOrUpdate(userGroup);

    }

    //组合sql 写入人群包
    //insert into  xxxx
    //select
    //bitmapAnd(
    //  (  bitmapAnd(
    //     (select groupBitmapMergeState(us) from user_tag_value_string where tag_code='tg_person_base_gender' and tag_value = '男')
    //    ,
    //     (select groupBitmapMergeState(us) from user_tag_value_string where tag_code='tg_person_base_agegroup' and tag_value in ( '90后'))
    //     )
    //   )
    //   ,
    //   (select groupBitmapMergeState(us) from user_tag_value_decimal where tag_code='tg_behavior_order_amount_7d' and tag_value > 500)
    // )
    //分解一下          单条子查询   ==>  bitmapAnd 组合多条子查询  ==>  select   ==> insert ==>执行
    public  void  genUserGroupUids(UserGroup userGroup ){

        String insertBitmapSQL = getInsertBitmapSQL(userGroup);
        this.baseMapper.insertBitmapCK(insertBitmapSQL);
    }



    public String getInsertBitmapSQL(UserGroup userGroup ){

        String selectSQL  = getSelectBitmapSQL(userGroup.getId().toString(), userGroup.getTagConditions(), userGroup.getBusiDate());
        String insertSQL= " insert into user_group "+selectSQL;
        return insertSQL;
    }


    public String getSelectBitmapSQL(String userGroupId,  List<TagCondition> tagConditionList , String busiDate){
        String selectSQL="";
        String bitmapSQL = getBitmapSQL(tagConditionList, busiDate);
        if(tagConditionList.size()==1){
            selectSQL="select  '"+userGroupId+"',   bts.us ,'"+busiDate+"' from ("+bitmapSQL+") bts";
        }else{
            selectSQL= " select '"+userGroupId+"',"+bitmapSQL+",'"+busiDate+"'";
        }
        System.out.println(selectSQL);
        return selectSQL;
    }


    //bitmapAnd(
    //   (  bitmapAnd(
    //      (subquery1 ) ,
    //      (subquery2)
    //     )
    //    )
    //  ,(subquery3)
    //  )


    // 通过bitmapAnd 函数 拼接多条子查询
    private String getBitmapSQL(List<TagCondition> tagConditionList,String busiDate){

        Map<String, TagInfo> tagInfoMap  = tagInfoService.getTagInfoMapWithCode();

        StringBuilder sqlBuilder=new StringBuilder();
        for (int i = 0; i < tagConditionList.size(); i++) {
            TagCondition tagCondition = tagConditionList.get(i);
            String subQuerySql = getSubQuerySql(tagCondition, busiDate, tagInfoMap);
            if(i==0){
                sqlBuilder.append(subQuerySql);
            }else if(i>0){
                sqlBuilder=  sqlBuilder.insert(0,"bitmapAnd( (").append("),("+subQuerySql+"))");
            }

        }

        return  sqlBuilder.toString();



    }



     //查询单条子查询
    // select groupBitmapMergeState(us) from user_tag_value_string
     // where tag_code='tg_person_base_gender' and tag_value = '男' and dt = '2020-06-14'
   //动态要素分析 ：
    // user_tag_value_string  ==> 根据标签类型查询 ==> 根据标签类型 查询不同的表
    // tg_person_base_gender ==>  tag_code
    // tag_value =  ==> 符号  ==>把operator 翻译成sql 符号
    // '男'  ==> tag_values ==> 要不加 括号、单引    如果是 in 或者  notin  加括号   如果数字就不加单引  字符串才加单引
    //  日期==> busiDate

    public  String getSubQuerySql(TagCondition tagCondition,String busiDate,Map<String ,TagInfo> tagInfoMap){
            String tagCode=tagCondition.getTagCode();
            TagInfo tagInfo = tagInfoMap.get(tagCode);
             String tagValueType = tagInfo.getTagValueType();
             //根据标签值选择查询的表
             String tableName=null;
             if( ConstCodes.TAG_VALUE_TYPE_LONG.equals(tagValueType)  ){
                 tableName="user_tag_value_long";
             }else if( ConstCodes.TAG_VALUE_TYPE_STRING.equals(tagValueType)  ) {
                 tableName = "user_tag_value_string";
             }else if( ConstCodes.TAG_VALUE_TYPE_DECIMAL.equals(tagValueType)  ) {
                 tableName = "user_tag_value_decimal";
             }else if( ConstCodes.TAG_VALUE_TYPE_DATE.equals(tagValueType)  ) {
                 tableName = "user_tag_value_date";
             }
           // tag_value =  ==> 符号  ==>把operator 翻译成sql 符号
           String conditionOperator = getConditionOperator(tagCondition.getOperator());
             //
            List<String> tagValues = tagCondition.getTagValues();
            String tagValuesSQL=null;
            if(  ConstCodes.TAG_VALUE_TYPE_STRING.equals(tagValueType)
                  ||ConstCodes.TAG_VALUE_TYPE_DATE.equals(tagValueType)){
                //需要拼接单引
                  tagValuesSQL  = "'" + StringUtils.join(tagValues, "','")+"'";
            }else{
                tagValuesSQL=StringUtils.join(tagValues, ",");
            }
           //mkstring  java ?
           if(tagCondition.getOperator().equals("in") ||tagCondition.getOperator().equals("nin")){
               tagValuesSQL= "("+tagValuesSQL+")";
           }

           // 日期
           String sql =  " select groupBitmapMergeState(us) as us from "+tableName+ ""
                   +" where tag_code='"+tagCode.toLowerCase()+"' " +
                   " and tag_value "+conditionOperator+" "+tagValuesSQL+
                   " and dt='"+busiDate+"'";

           return  sql;

    }





    // 把中文的操作代码转换为 判断符号
    private  String getConditionOperator(String operator){
        switch (operator){
            case "eq":
                return "=";
            case "lte":
                return "<=";
            case "gte":
                return ">=";
            case "lt":
                return "<";
            case "gt":
                return ">";
            case "neq":
                return "<>";
            case "in":
                return "in";
            case "nin":
                return "not in";
        }
        throw  new RuntimeException("操作符不正确");
    }




    @Override
    public void saveUserGroupUidsToReids(UserGroup userGroup) {
            //redis
        //type?  set    key ? user_group:id     value?  uid   score/field? 无
        // 写入api  sadd   读取api？  smembers查全体 sismembers 判存
        // 过期时间？ 不过期
        //1 根据主键查询 clickhouse的人群包结果
        // 如何防止查询出 旧数据 //1 等待几秒( 负面效果 影响用户体验，等待几秒说不清楚)
        // 2 optimize table xx final  (老版本可以 ，新版本该操作也是异步的, 会影响ck的性能)
        // 3 解耦 redis的 执行结果不依赖ck的之前的计算
        Long userGroupId = userGroup.getId();
    //    List<String> uidList = this.baseMapper.selectBitMapArrayById(userGroupId.toString());

        // 3 解耦 redis的 执行结果不依赖ck的之前的计算 ,重新组合bitmapSQL进行计算
        String selectSQL="";
        String bitmapSQL = getBitmapSQL(userGroup.getTagConditions(), userGroup.getBusiDate());
        //2 组合bitmapCardinality
        if(userGroup.getTagConditions().size()==1){
            selectSQL="select    arrayJoin(bitmapToArray( ( bts.us ))) from ("+bitmapSQL+") bts";
        }else{
            selectSQL= " select arrayJoin(bitmapToArray(  "+bitmapSQL+"))";
        }
        List<String> uidList = this.baseMapper.selectBitMapArraySQLById(selectSQL);

        System.out.println(uidList);
        // 2 写入redis

        Jedis jedis = RedisUtil.getJedis();
        String userGroupKey="user_group:"+userGroupId;
        String[] uids = uidList.toArray(new String[]{});
        jedis.sadd(userGroupKey,uids);
        jedis.close();

    }


    // 评估人数
    // 1 利用原有的bitmapSQL
    //2 组合bitmapCardinality
    //3 执行
    public Long evaluateNum(UserGroup userGroup){
      // 1 利用原有的bitmapSQL
        // select bitmapCardinality( bitmapAnd(....)) ;
        String selectSQL="";
        String bitmapSQL = getBitmapSQL(userGroup.getTagConditions(), userGroup.getBusiDate());
        //2 组合bitmapCardinality
        if(userGroup.getTagConditions().size()==1){
            selectSQL="select    bitmapCardinality( bts.us ) from ("+bitmapSQL+") bts";
        }else{
            selectSQL= " select bitmapCardinality( "+bitmapSQL+")";
        }
        System.out.println(selectSQL);
        //3 执行
        Long num = this.baseMapper.selectCardi(selectSQL);
        return num;
    }


    //清除clickhouse对应的分群
    public void  clearUserGroupUidCk(String  userGroupId){
         this.baseMapper.deleteUserGroup(userGroupId);
    }

    //清除clickhouse对应的分群
    public void  clearUserGroupUidRedis(String  userGroupId){
        Jedis jedis = RedisUtil.getJedis();
        String userGroupKey="user_group:"+userGroupId;
        jedis.del(userGroupKey);
        jedis.close();
    }
}
